package com.tchepannou.limbe.web.servlet;

import com.tchepannou.limbe.annotation.Template;
import com.tchepannou.limbe.engine.Engine;
import com.tchepannou.limbe.interceptor.TemplateInterceptor;
import com.tchepannou.limbe.service.RenderService;
import com.tchepannou.limbe.web.ActionController;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

/**
 * Page decorator
 * 
 * @author herve
 */
public class TemplateFilter
    implements Filter
{
    //-- Static Attributes
    private static final String TEMPLATE_PREFIX = "/template/";


    private FilterConfig _config;


    //-- Filter overrides
    @Override
    public void doFilter (ServletRequest req, ServletResponse res, FilterChain chain)
        throws IOException,
               ServletException
    {
        if ( req instanceof HttpServletRequest )
        {
            HttpServletRequest request = ( HttpServletRequest ) req;
            if ( res instanceof HttpServletResponse )
            {
                HttpServletResponse response = ( HttpServletResponse ) res;
                doFilter (request, response, chain);
            }
        }
    }

    @Override
    public void init (FilterConfig config)
    {
        _config = config;
    }

    @Override
    public void destroy ()
    {
        _config = null;
    }


    //-- Private methods
    private void doFilter (HttpServletRequest request, HttpServletResponse response, FilterChain chain)
        throws IOException,
               ServletException
    {
        /* Process the request */
        BufferedResponseWrapper wrappedResponse = new BufferedResponseWrapper (response);
        try
        {
            chain.doFilter (request, wrappedResponse);
        }
        finally
        {
            wrappedResponse.finishResponse ();
        }
        byte[] content = wrappedResponse.toByteArray ();

        /* Apply the template */
        if (wrappedResponse.getStatus () != 302 && canDecorate (wrappedResponse) )    // dont apply the template when redirecting
        {
            Template template = (Template)request.getAttribute (TemplateInterceptor.ATTR_TEMPLATE);
            if ( template != null)
            {
                try
                {
                    Writer writer = response.getWriter ();
                    ActionController controller = (ActionController)request.getAttribute (TemplateInterceptor.ATTR_CONTROLLER);
                    applyTemplate (request, template, content, controller, writer);
                }
                catch (IllegalStateException e)
                {
                    response.getOutputStream ().write (content);
                }
            }
            else
            {
                try
                {
                    response.getOutputStream ().write (content);
                }
                catch (IllegalStateException e)
                {
                    response.getWriter ().write (new String (content));
                }
            }
        }
    }

    private void applyTemplate (HttpServletRequest request, Template template, byte[] content, ActionController controller, Writer writer)
        throws IOException,
               ServletException
    {
        String pathInfo = request.getPathInfo ();
        int i = pathInfo != null ? pathInfo.lastIndexOf (".") : -1;
        String ext = i > 0 ? pathInfo.substring (i) : "";
        String path = TEMPLATE_PREFIX + template.name () + ext;
        
        Map data = new HashMap ();
        data.putAll (controller.getViewVariables ());
        data.put ("__template", template);
        data.put ("__content", new String(content));

        getRenderService ().render (path, data, writer);
    }

    private RenderService getRenderService ()
    {
        Engine engine = (Engine)_config.getServletContext().getAttribute(LimbeServlet.ENGINE_KEY);
        RenderService s = engine.findService (RenderService.class);
        if (s == null)
        {
            throw new IllegalStateException ("Service not found: " + RenderService.class.getName ());
        }

        return s;
    }

    private boolean canDecorate (HttpServletResponse response)
    {
        String contentType = response.getContentType ();
        if (contentType == null )
        {
            return false;
        }
        else
        {
            return contentType.startsWith ("text/")
                || contentType.startsWith ("application/json");
        }
    }
}
